package cn.legend.travel.tour.team.service.impl;

import cn.legend.travel.common.pojo.vo.PageData;
import cn.legend.travel.tour.team.dao.cache.ITourCacheRepository;
import cn.legend.travel.tour.team.dao.persist.mapper.TourMapper;
import cn.legend.travel.tour.team.dao.persist.repository.ITourRepository;
import cn.legend.travel.tour.team.dao.search.ITourSearchRepository;
import cn.legend.travel.tour.team.dao.search.TourElasticSearchRepository;
import cn.legend.travel.tour.team.pojo.entity.Tour;
import cn.legend.travel.tour.team.pojo.param.RequestParams;
import cn.legend.travel.tour.team.pojo.po.TourSearchPO;
import cn.legend.travel.tour.team.pojo.vo.TourDetailVO;
import cn.legend.travel.tour.team.pojo.vo.TourHotVO;
import cn.legend.travel.tour.team.pojo.vo.TourSearchVO;
import cn.legend.travel.tour.team.pojo.vo.TourVO;
import cn.legend.travel.tour.team.service.ITourService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class TourServiceImpl implements ITourService {
    @Autowired
    ITourRepository tourRepository;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    ITourCacheRepository tourCacheRepository;
    @Autowired
    TourElasticSearchRepository tourElasticSearchRepository;
    @Autowired
    ITourSearchRepository tourSearchRepository;
    @Value("${legend.dao.default-query-page-size}")
    private Integer pageSize;

    @Override
    public PageData<TourVO> listForGeneral(Integer pageNum) {
        log.debug("【正在处理综合排序业务】");
        PageData<TourVO> tourList = tourCacheRepository.getTourList(0,pageNum);
        if(tourList ==null ||tourList.getList().size() == 0){
            PageData<TourVO> tourPOPageData = tourRepository.listForGeneral(pageNum);
            tourCacheRepository.setTourList(0,pageNum,tourPOPageData);
            return tourPOPageData;
        }
        return tourList;

    }

    @Override
    public PageData<TourVO> listForSaleCount(Integer pageNum) {
        log.debug("【正在处理销量优先业务】");
        PageData<TourVO> tourList = tourCacheRepository.getTourList(1,pageNum);
        if(tourList ==null || tourList.getList().size() == 0){
            PageData<TourVO> tourPOPageData = tourRepository.listForSaleCount(pageNum);
            tourCacheRepository.setTourList(1,pageNum,tourPOPageData);
            return tourPOPageData;
        }
        return tourList;
    }

    @Override
    public PageData<TourVO> listForPriceASC(Integer pageNum) {
        log.debug("【正在处理价格从低到高业务】");
        PageData<TourVO> tourList = tourCacheRepository.getTourList(2,pageNum);
        if( tourList ==null ||tourList.getList().size() == 0){
            PageData<TourVO> tourPOPageData = tourRepository.listForPriceASC(pageNum);
            tourCacheRepository.setTourList(2,pageNum,tourPOPageData);
            return tourPOPageData;
        }
        return tourList;
    }

    @Override
    public PageData<TourVO> listForPriceDesc(Integer pageNum) {
        log.debug("【正在处理从高到低业务】");
        PageData<TourVO> tourList = tourCacheRepository.getTourList(3,pageNum);
        if(tourList ==null || tourList.getList().size() == 0){
            PageData<TourVO> tourPOPageData = tourRepository.listForPriceDesc(pageNum);
            tourCacheRepository.setTourList(3,pageNum,tourPOPageData);
            return tourPOPageData;
        }
        return tourList;
    }

    @Override
    public List<TourHotVO> listForHotItem() {
        log.debug("【正在热销业务】");
        List<TourHotVO> tourHotList = tourCacheRepository.getTourHotList(4, pageSize);
        if(tourHotList.size() == 0){
            List<TourHotVO> tourHotVOS = tourRepository.listForHotItem(pageSize);
            tourCacheRepository.setTourHotList(4,pageSize,tourHotVOS);
            return tourHotVOS;
        }
        return tourHotList;
    }

    @Override
    public PageData<TourVO> search(RequestParams params) {
        try {
            // 1.准备Request
            SearchRequest request = new SearchRequest("tour");
            // 2.准备请求参数
            // 2.1.query
            buildBasicQuery(params, request);
            // 2.2.分页
            int page = params.getPage();
            int size = params.getSize();
            request.source().from((page - 1) * size).size(size);
            //2.3 排序
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            Integer sortBy = params.getSortBy();
            if(sortBy == null || sortBy < 0|| sortBy > 4){
                sortBy = 0;
            }
            switch (sortBy){
                case 0:
                    //设置排序规则为按销量的从高到低排序
                    sourceBuilder.sort("saleCount",SortOrder.DESC);
                    //设置排序规则为按价格的从低到高排序
                    sourceBuilder.sort("basePrice", SortOrder.ASC);
                    break;
                case 1 :
                    //设置排序规则为按销量的从高到低排序
                    sourceBuilder.sort("saleCount",SortOrder.DESC);
                    break;
                case 2:
                    //设置排序规则为按价格的从低到高排序
                    sourceBuilder.sort("basePrice", SortOrder.ASC);
                    break;
                case 3:
                    //设置排序规则为按价格的从高到低排序
                    sourceBuilder.sort("basePrice",SortOrder.DESC);
                    break;
            }
            request.source(sourceBuilder);
            // 3.发送请求
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);


            return handleResponse(response);

        } catch (IOException e) {
            throw new RuntimeException("搜索数据失败", e);
        }
    }

    @Override
    public PageData<TourSearchVO> search(String keyword, String departure, Integer pageNum) {
        log.debug("开始处理【搜索旅游团】的业务，关键词：{}, 页码：{}", keyword, pageNum);
        return tourSearchRepository.search(keyword,departure,pageNum);
    }

    @Override
    public PageData<TourSearchVO> searchByType(String type, Integer pageNum) {
        log.debug("开始处理【根据搜索旅游团】的业务，旅游团类型：{}, 页码：{}", type, pageNum);
        return tourSearchRepository.searchByType(type, pageNum);
    }

    @Override
    public void rebuildSearch() {
        log.info("开始执行【重新创建旅游团的搜索数据】的业务");
        tourElasticSearchRepository.deleteAll();
        Integer pageNum = 1;
        Integer maxPage;
        PageData<TourSearchPO> pageData;
        do {
            pageData = tourRepository.listSearch(pageNum,pageSize);
            maxPage = pageData.getMaxPage();
            tourElasticSearchRepository.saveAll(pageData.getList());
            pageNum++;
        }while (pageNum<= maxPage);


    }

    @Override
    public TourDetailVO getItemByGuideId(Long guideId) {
        log.info("开始执行【旅游团详情】的业务");
        return tourRepository.getItemByGuideId(guideId);
    }

    //准备请求参数的功能函数
    private void buildBasicQuery(RequestParams params, SearchRequest request) {
        // 1.准备Boolean查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 1.1.关键字搜索，match查询，放到must中
        String key = params.getKeyword();
        if (StringUtils.isNotBlank(key)) {
            // 不为空，根据关键字查询
            boolQuery.must(QueryBuilders.matchQuery("all", key));
        } else {
            // 为空，查询所有
            boolQuery.must(QueryBuilders.matchAllQuery());
        }
        // 1.2.旅游团类型
        String type = params.getType();
        if (StringUtils.isNotBlank(type)) {
            boolQuery.filter(QueryBuilders.termQuery("type", type));
        }
        // 1.3.目的城市
        String city = params.getCity();
        if (StringUtils.isNotBlank(city)) {
            boolQuery.filter(QueryBuilders.termQuery("departure", city));
        }
        // 2.算分函数查询
        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
                boolQuery, // 原始查询，boolQuery
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{ // function数组
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                //这里还不太清楚 暂时不要了
                               // QueryBuilders.termQuery("isAD", true), // 过滤条件
                                ScoreFunctionBuilders.weightFactorFunction(10) // 算分函数
                        )
                }
        );
        // 3.设置查询条件
        request.source().query(functionScoreQuery);
    }
    private PageData<TourVO> handleResponse(SearchResponse response) {
        SearchHits searchHits = response.getHits();
        // 4.1.总条数
        long total = searchHits.getTotalHits().value;
        // 4.2.获取文档数组
        SearchHit[] hits = searchHits.getHits();
        // 4.3.遍历
        List<TourVO> tours = new ArrayList<>(hits.length);
        for (SearchHit hit : hits) {
            // 4.4.获取source
            String json = hit.getSourceAsString();
            // 4.5.反序列化，非高亮的
            TourVO tour = JSON.parseObject(json, TourVO.class);
            // 4.6.处理高亮结果
            // 1)获取高亮map
            Map<String, HighlightField> map = hit.getHighlightFields();
            if (map != null && !map.isEmpty()) {
                // 2）根据字段名，获取高亮结果
                HighlightField highlightField = map.get("title");
                if (highlightField != null) {
                    // 3）获取高亮结果字符串数组中的第1个元素
                    String title = highlightField.getFragments()[0].toString();
                    // 4）把高亮结果放到HotelDoc中
                    tour.setTitle(title);
                }
            }
            // 4.9.放入集合
            tours.add(tour);
        }
        PageData<TourVO> tourVOPageData = new PageData<>();
        tourVOPageData.setTotal(total);
        tourVOPageData.setPageSize(pageSize);
        tourVOPageData.setList(tours);
        return tourVOPageData;
    }
}

